<template>
	<div class="tree-transfer">
		<!-- 左侧面板 -->
		<div class="transfer-panel">
			<div class="transfer-panel-header">
				<span class="panel-title">{{ leftTitle }}</span>
				<span class="panel-count">{{ leftCheckedCount }}/{{ leftTotalCount }}</span>
			</div>
			<div class="transfer-panel-body">
				<div v-if="leftTreeData.length === 0" class="empty-state">无数据</div>
				<el-tree
					v-else
					ref="leftTreeRef"
					:data="leftTreeData"
					:props="treeProps"
					:default-expand-all="defaultExpandAll"
					show-checkbox
					node-key="Value"
					:check-strictly="checkStrictly"
					@check="handleLeftCheck"
					:class="['transfer-tree', { 'is-disabled': disabled }]"
				>
					<template #default="{ node, data }">
						<span class="tree-node-label">{{ node.label }}</span>
					</template>
				</el-tree>
			</div>
		</div>

		<!-- 中间按钮 -->
		<div class="transfer-buttons">
			<el-button type="primary" :icon="ArrowRight" :disabled="disabled || !hasLeftChecked" @click="moveToRight" :title="'移动到右侧'"></el-button>
			<el-button type="primary" :icon="ArrowLeft" :disabled="disabled || !hasRightChecked" @click="moveToLeft" :title="'移动到左侧'"></el-button>
		</div>

		<!-- 右侧面板 -->
		<div class="transfer-panel">
			<div class="transfer-panel-header">
				<span class="panel-title">{{ rightTitle }}</span>
				<span class="panel-count">{{ rightCheckedCount }}/{{ rightTotalCount }}</span>
			</div>
			<div class="transfer-panel-body">
				<div v-if="rightTreeData.length === 0" class="empty-state">无数据</div>
				<el-tree
					v-else
					ref="rightTreeRef"
					:data="rightTreeData"
					:props="treeProps"
					:default-expand-all="defaultExpandAll"
					show-checkbox
					node-key="Value"
					:check-strictly="checkStrictly"
					@check="handleRightCheck"
					:class="['transfer-tree', { 'is-disabled': disabled }]"
				>
					<template #default="{ node, data }">
						<span class="tree-node-label">{{ node.label }}</span>
					</template>
				</el-tree>
			</div>
		</div>
	</div>
</template>

<script setup lang="ts">
import { ref, computed, watch, nextTick } from 'vue';
import { ArrowRight, ArrowLeft } from '@element-plus/icons-vue';

interface TreeNode {
	Value: string | number;
	Text: string;
	Children?: TreeNode[];
	children?: TreeNode[];
	[key: string]: any;
}

interface Props {
	/** 左侧标题 */
	leftTitle?: string;
	/** 右侧标题 */
	rightTitle?: string;
	/** 数据源 */
	data?: TreeNode[];
	/** 已选中的值（右侧数据） */
	modelValue?: (string | number)[];
	/** 树形结构属性配置 */
	props?: {
		children?: string;
		label?: string;
		value?: string;
	};
	/** 是否默认展开所有节点 */
	defaultExpandAll?: boolean;
	/** 是否严格的遵循父子节点不关联 */
	checkStrictly?: boolean;
	/** 是否禁用 */
	disabled?: boolean;
}

const props = withDefaults(defineProps<Props>(), {
	leftTitle: '列表 1',
	rightTitle: '列表 2',
	data: () => [],
	modelValue: () => [],
	props: () => ({
		children: 'Children',
		label: 'Text',
		value: 'Value',
	}),
	defaultExpandAll: false,
	checkStrictly: false,
	disabled: false,
});

const emit = defineEmits<{
	'update:modelValue': [value: (string | number)[]];
}>();

const leftTreeRef = ref();
const rightTreeRef = ref();
const leftCheckedKeys = ref<(string | number)[]>([]);
const rightCheckedKeys = ref<(string | number)[]>([]);

// 树形结构属性配置
const treeProps = computed(() => ({
	children: props.props?.children || 'Children',
	label: props.props?.label || 'Text',
	value: props.props?.value || 'Value',
}));

// 获取子节点字段名（支持 Children 和 children）
const getChildrenField = (node: TreeNode): TreeNode[] | undefined => {
	return node.Children || node.children;
};

// 获取所有节点的值（包括子节点）
const getAllNodeValues = (nodes: TreeNode[]): (string | number)[] => {
	const values: (string | number)[] = [];
	const traverse = (nodeList: TreeNode[]) => {
		nodeList.forEach((node) => {
			values.push(node.Value);
			const children = getChildrenField(node);
			if (children && children.length > 0) {
				traverse(children);
			}
		});
	};
	traverse(nodes);
	return values;
};

// 从树中移除节点
const removeNodesFromTree = (tree: TreeNode[], keysToRemove: (string | number)[]): TreeNode[] => {
	return tree
		.map((node) => {
			if (keysToRemove.includes(node.Value)) {
				return null;
			}
			const newNode = { ...node };
			const children = getChildrenField(newNode);
			if (children && children.length > 0) {
				if (newNode.Children) {
					newNode.Children = removeNodesFromTree(children, keysToRemove);
				} else if (newNode.children) {
					newNode.children = removeNodesFromTree(children, keysToRemove);
				}
			}
			return newNode;
		})
		.filter((node): node is TreeNode => node !== null);
};

// 从树中提取节点（优化：避免重复提取父子节点）
// 核心逻辑：如果父节点在 keys 中，只提取父节点（包含完整的子节点树），不再单独提取子节点
const extractNodesFromTree = (tree: TreeNode[], keys: (string | number)[]): TreeNode[] => {
	if (keys.length === 0) return [];

	const extracted: TreeNode[] = [];
	const keysSet = new Set(keys);

	// 递归提取节点：如果节点在 keys 中，提取该节点（包含完整的子节点树），不再单独提取子节点
	const extractNode = (node: TreeNode, parentInKeys = false): TreeNode | null => {
		const nodeInKeys = keysSet.has(node.Value);
		const children = getChildrenField(node);

		// 如果父节点在 keys 中，且当前节点也在 keys 中，说明当前节点是父节点的子节点
		// 这种情况下，我们不应该单独提取子节点，因为父节点已经包含了它
		if (parentInKeys && nodeInKeys) {
			// 父节点已经在 keys 中，当前节点是子节点，不单独提取
			// 但需要保持树结构，所以返回 null（由父节点处理）
			return null;
		}

		if (nodeInKeys) {
			// 节点在 keys 中，且父节点不在 keys 中，创建节点副本（包含完整的子节点树结构）
			const nodeCopy: TreeNode = { ...node };

			// 如果有子节点，保持完整的子节点树结构（用于树形显示）
			// 注意：即使子节点也在 keys 中，我们也保持完整的树结构，不进行递归提取
			if (children && children.length > 0) {
				// 保持原有的子节点结构，不进行递归提取，避免重复
				if (node.Children) {
					nodeCopy.Children = children;
				} else {
					nodeCopy.children = children;
				}
			}

			return nodeCopy;
		} else {
			// 节点不在 keys 中，检查子节点
			if (children && children.length > 0) {
				const extractedChildren: TreeNode[] = [];
				children.forEach((child) => {
					// 传递 parentInKeys=false，因为当前节点不在 keys 中
					const extractedChild = extractNode(child, false);
					if (extractedChild) {
						extractedChildren.push(extractedChild);
					}
				});

				// 如果有子节点被提取，创建一个包含这些子节点的节点
				if (extractedChildren.length > 0) {
					const nodeCopy: TreeNode = { ...node };
					if (node.Children) {
						nodeCopy.Children = extractedChildren;
					} else {
						nodeCopy.children = extractedChildren;
					}
					return nodeCopy;
				}
			}

			return null;
		}
	};

	// 遍历树，提取节点
	tree.forEach((node) => {
		const extractedNode = extractNode(node, false);
		if (extractedNode) {
			extracted.push(extractedNode);
		}
	});

	return extracted;
};

// 左侧树数据（原始数据减去已选中的数据，避免重复）
const leftTreeData = computed(() => {
	// 如果数据为空，直接返回空数组
	if (!props.data || props.data.length === 0) {
		return [];
	}

	try {
		const dataCopy = JSON.parse(JSON.stringify(props.data));
		if (props.modelValue.length === 0) {
			return dataCopy;
		}
		// 去重：移除已选中的数据
		const uniqueSelected = Array.from(new Set(props.modelValue));
		return removeNodesFromTree(dataCopy, uniqueSelected);
	} catch (error) {
		console.error('treeTransfer: 处理左侧树数据时出错', error);
		return [];
	}
});

// 右侧树数据（已选中的数据）
const rightTreeData = computed(() => {
	if (props.modelValue.length === 0) {
		return [];
	}

	// 如果数据为空，返回空数组（数据可能还在加载中）
	if (!props.data || props.data.length === 0) {
		return [];
	}

	try {
		const dataCopy = JSON.parse(JSON.stringify(props.data));
		// 去重：确保没有重复的 key
		const uniqueSelected = Array.from(new Set(props.modelValue));
		const extracted = extractNodesFromTree(dataCopy, uniqueSelected);

		// 确保数据格式正确：如果数据使用 Children，el-tree 也需要能识别
		// el-tree 的 props 配置会处理字段映射，但我们需要确保数据结构正确
		return extracted;
	} catch (error) {
		console.error('treeTransfer: 处理右侧树数据时出错', error);
		return [];
	}
});

// 左侧选中数量
const leftCheckedCount = computed(() => leftCheckedKeys.value.length);
// 右侧选中数量
const rightCheckedCount = computed(() => rightCheckedKeys.value.length);

// 左侧总数
const leftTotalCount = computed(() => getAllNodeValues(leftTreeData.value).length);
// 右侧总数
const rightTotalCount = computed(() => getAllNodeValues(rightTreeData.value).length);

// 是否有左侧选中
const hasLeftChecked = computed(() => leftCheckedKeys.value.length > 0);
// 是否有右侧选中
const hasRightChecked = computed(() => rightCheckedKeys.value.length > 0);

// 处理左侧树选中
const handleLeftCheck = (data: TreeNode, checked: { checkedKeys: (string | number)[]; halfCheckedKeys: (string | number)[] }) => {
	if (props.disabled) return;
	leftCheckedKeys.value = checked.checkedKeys;
};

// 处理右侧树选中
const handleRightCheck = (data: TreeNode, checked: { checkedKeys: (string | number)[]; halfCheckedKeys: (string | number)[] }) => {
	if (props.disabled) return;
	rightCheckedKeys.value = checked.checkedKeys;
};

// 移动到右侧（去重处理）
const moveToRight = () => {
	if (leftCheckedKeys.value.length === 0) return;
	// 去重：过滤掉已经存在的值，避免重复
	const newKeys = leftCheckedKeys.value.filter((key) => !props.modelValue.includes(key));
	if (newKeys.length === 0) {
		// 如果所有选中的项都已存在，清空选中状态
		leftCheckedKeys.value = [];
		nextTick(() => {
			leftTreeRef.value?.setCheckedKeys([]);
		});
		return;
	}
	const newValue = [...props.modelValue, ...newKeys];
	// 再次去重，确保没有重复
	const uniqueValue = Array.from(new Set(newValue));
	emit('update:modelValue', uniqueValue);
	leftCheckedKeys.value = [];
	nextTick(() => {
		leftTreeRef.value?.setCheckedKeys([]);
	});
};

// 移动到左侧（去重处理）
const moveToLeft = () => {
	if (rightCheckedKeys.value.length === 0) return;
	// 直接过滤掉选中的值
	const newValue = props.modelValue.filter((key) => !rightCheckedKeys.value.includes(key));
	emit('update:modelValue', newValue);
	rightCheckedKeys.value = [];
	nextTick(() => {
		rightTreeRef.value?.setCheckedKeys([]);
	});
};

// 监听 modelValue 变化，自动去重
watch(
	() => props.modelValue,
	(newVal) => {
		// 自动去重处理
		const uniqueValues = Array.from(new Set(newVal));
		if (uniqueValues.length !== newVal.length) {
			// 如果有重复，更新为去重后的值
			nextTick(() => {
				emit('update:modelValue', uniqueValues);
			});
		}
		// 清空选中状态
		nextTick(() => {
			if (rightTreeRef.value) {
				rightTreeRef.value.setCheckedKeys([]);
			}
			if (leftTreeRef.value) {
				leftTreeRef.value.setCheckedKeys([]);
			}
		});
	},
	{ deep: true }
);

// 监听 data 变化，确保数据更新时组件能正确响应
watch(
	() => props.data,
	() => {
		// 当数据源变化时，清空选中状态，避免状态不一致
		nextTick(() => {
			if (rightTreeRef.value) {
				rightTreeRef.value.setCheckedKeys([]);
			}
			if (leftTreeRef.value) {
				leftTreeRef.value.setCheckedKeys([]);
			}
		});
	},
	{ deep: true }
);
</script>

<style scoped lang="scss">
.tree-transfer {
	display: flex;
	align-items: flex-start;
	width: 77%;
	gap: 12px;
	--el-transfer-item-height: 28px;

	// 响应式布局：小屏幕垂直排列，大屏幕水平排列
	flex-direction: column;

	@media (min-width: 768px) {
		flex-direction: row;
		align-items: center;
	}

	.transfer-panel {
		flex: 1;
		border: 1px solid var(--el-border-color-lighter, #e4e7ed);
		border-radius: 4px;
		overflow: hidden;
		background: var(--el-bg-color-overlay, #fff);
		min-width: 0;
		display: flex;
		flex-direction: column;
		height: 300px;
		width: 100%;

		@media (min-width: 768px) {
			height: 320px;
			min-width: 200px;
		}

		.transfer-panel-header {
			display: flex;
			justify-content: space-between;
			align-items: center;
			padding: 0 12px;
			height: 36px;
			background: var(--el-fill-color-light, #f5f7fa);
			border-bottom: 1px solid var(--el-border-color-lighter, #e4e7ed);
			box-sizing: border-box;
			flex-shrink: 0;

			.panel-title {
				font-size: 13px;
				color: var(--el-text-color-primary, #303133);
				font-weight: 500;
			}

			.panel-count {
				font-size: 12px;
				color: var(--el-text-color-secondary, #909399);
			}
		}

		.transfer-panel-body {
			flex: 1;
			overflow: auto;
			padding: 4px 0;
			background: var(--el-bg-color-overlay, #fff);
			box-sizing: border-box;
			min-height: 0;

			&::-webkit-scrollbar {
				width: 6px;
				height: 6px;
			}

			&::-webkit-scrollbar-thumb {
				border-radius: 3px;
				background: var(--el-border-color, #c0c4cc);
			}

			&::-webkit-scrollbar-track {
				background: var(--el-fill-color-lighter, #f5f7fa);
			}

			.empty-state {
				display: flex;
				align-items: center;
				justify-content: center;
				height: 100%;
				color: var(--el-text-color-placeholder, #c0c4cc);
				font-size: 13px;
				min-height: 80px;
			}

			.transfer-tree {
				:deep(.el-tree) {
					background-color: transparent;
					padding: 0;
				}

				:deep(.el-tree-node) {
					margin: 0;
				}

				:deep(.el-tree-node__content) {
					height: var(--el-transfer-item-height);
					line-height: var(--el-transfer-item-height);
					padding: 0 8px;
					margin: 0 4px;
					margin-bottom: 1px;
					cursor: pointer;

					&:hover {
						background-color: var(--el-fill-color-light, #f5f7fa);
					}

					&.is-checked {
						background-color: var(--el-color-primary-light-9, #ecf5ff);
					}
				}

				&.is-disabled {
					:deep(.el-tree-node__content) {
						cursor: not-allowed;
						opacity: 0.6;

						&:hover {
							background-color: transparent;
						}
					}

					:deep(.el-checkbox) {
						cursor: not-allowed;
						pointer-events: none;
					}
				}

				:deep(.el-checkbox) {
					margin-right: 8px;
				}

				:deep(.el-tree-node__expand-icon) {
					padding: 0;
					width: 14px;
					height: 14px;
					font-size: 12px;
					margin-right: 4px;
				}

				:deep(.el-tree-node__expand-icon.is-leaf) {
					opacity: 0;
				}

				:deep(.el-tree-node__label) {
					font-size: 13px;
					color: var(--el-text-color-regular, #606266);
					line-height: var(--el-transfer-item-height);
					user-select: none;
				}

				.tree-node-label {
					font-size: 13px;
					color: var(--el-text-color-regular, #606266);
					line-height: var(--el-transfer-item-height);
					user-select: none;
				}
			}
		}
	}

	.transfer-buttons {
		display: flex;
		flex-direction: row;
		justify-content: center;
		align-items: center;
		gap: 8px;
		padding: 8px 0;
		flex-shrink: 0;
		width: 100%;

		@media (min-width: 768px) {
			flex-direction: row;
			justify-content: center;
			padding: 0 12px;
			width: auto;
		}

		.el-button {
			width: 32px;
			height: 32px;
			min-width: 32px;
			min-height: 32px;
			padding: 0;
			border-radius: 4px;
			display: inline-flex;
			align-items: center;
			justify-content: center;
			transition: opacity 0.2s;

			&:hover:not(:disabled) {
				opacity: 0.8;
			}

			&:disabled {
				opacity: 0.4;
				cursor: not-allowed;
			}

			:deep(.el-icon) {
				font-size: 14px;
			}
		}
	}

	// 移动端优化
	@media (max-width: 767px) {
		gap: 8px;

		.transfer-panel {
			height: 250px;
		}

		.transfer-panel-header {
			padding: 0 10px;
			height: 32px;

			.panel-title {
				font-size: 12px;
			}

			.panel-count {
				font-size: 11px;
			}
		}

		.transfer-panel-body {
			padding: 3px 0;

			.transfer-tree {
				:deep(.el-tree-node__content) {
					height: 26px;
					line-height: 26px;
					padding: 0 6px;
				}

				:deep(.el-tree-node__label),
				.tree-node-label {
					font-size: 12px;
				}
			}
		}
	}
}
</style>
